package com.qcby.demo.proxyaop.aop;

import com.alibaba.fastjson.JSONObject;
import com.qcby.demo.proxyaop.entity.OperatorLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @className: ExpressionAop
 * @description:
 * @author: lxt
 * @create: 2021-05-12 17:38
 **/

/**
 *  这是一个切面：
 *      1、切面是如何  和  controller关联起来的？  =>  切入点（Pointcut）
 *      2、切面里面的各个方法是什么意思，什么时候执行的
 *      3、
 *
 */
@Slf4j
// 切面注解   => 声明是一个切面
@Aspect
// 声明是一个Bean组件
//@Component
public class ExpressionAop {

    @Autowired
    private HttpServletRequest request;


    //定义切点，注解作为切入点
    @Pointcut("execution(public * com.qcby.demo.proxyaop.controller..*.*(..))")
    public void viewRecordsPoinCut() {

    }
//    @Pointcut("execution(public * com.qcby.demo.proxyaop.controller..*.*(..))")
//    public void viewRecordsPoinCut1() {
//
//    }
    /**
     * before 目标方法执行前执行，前置通知
     * after 目标方法执行后执行，后置通知
     * after returning 目标方法返回时执行 ，后置返回通知
     * after throwing 目标方法抛出异常时执行 异常通知
     * around 在目标函数执行中执行，可控制目标函数是否执行，环绕通知
     */

    @Before("viewRecordsPoinCut()")
    public void before(JoinPoint joinPoint) throws Throwable {
        log.info("进入Before通知....");
    }

    @After("viewRecordsPoinCut()")
    public void after(JoinPoint joinPoint) throws Throwable {
        log.info("进入After通知....");
    }

    @AfterReturning("viewRecordsPoinCut()")
    public void afterReturning(JoinPoint joinPoint) throws Throwable {
        log.info("进入AfterReturning通知....");
    }

    @AfterThrowing("viewRecordsPoinCut()")
    public void afterThrowing(JoinPoint joinPoint) throws Throwable {
        log.info("进入AfterThrowing通知....");
    }



    @Around("viewRecordsPoinCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("进入Around通知....=>前置增强");
        OperatorLog operatorLog = new OperatorLog();
        // 可以从session获取 / token获取
        operatorLog.setUserId(1L);
        /**
         * 进行业务操作
         */
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();
        log.info("method-name:{}",method.getName());
        String clz = joinPoint.getTarget().getClass().toString();
        operatorLog.setMethodName(clz +"."+ method.getName());
        operatorLog.setMethodPath(request.getRequestURI());
        //获取切入点方法参数
        Object[] objects = joinPoint.getArgs();
        String[] paramNames =  signature.getParameterNames();
        Map<String,Object> map = new HashMap<>();
        for (int i =0;i<paramNames.length;i++){
            map.put(paramNames[i],objects[i]);
        }
        log.info("入参：{}",map);
        // 入参转化成json
        operatorLog.setInParams(JSONObject.toJSONString(map));
        // 执行目标方法  获取返回值
        Object r = joinPoint.proceed();
        log.info("出参：{}",r);
        // 如果我们封装了通用的返回结果可以直接转json
        operatorLog.setOutParams(r.toString());
        operatorLog.setCreateTime(LocalDateTime.now());
        log.info("进入Around通知....=>后置增强=>出参：{}",r);
        // 插入数据库，操作日志：
        System.out.println(operatorLog);
        return r;
    }

}
